﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using LabChecker.Models.Programs;
using LabChecker.Models.Programs.CompilationResult;
using RunExceptions = LabChecker.Models.Programs.Exceptions;

namespace LabChecker.Models
{
    public class Task
    {
        public int Id { get; set; }
        [Required]
        public TaskDescription Description { get; set; }        
        [Required]
        public virtual Subject Subject { get; set; }
        public virtual List<TestCase> TestCases { get; set; }

        public Task()
        {
            Description = new TaskDescription();
            TestCases = new List<TestCase>();
        }

        public virtual CheckResults.TaskBase Check(Program program)
        {
            var compilationResult = program.Compile();
            if (compilationResult is Error)
            {
                var error = compilationResult as Error;
                return new CheckResults.Task.CompilationError(error.Line, error.Column, error.ErrorMessage);
            }

            for (int i = 0; i < TestCases.Count; i++)
            {
                var currentTestCase = TestCases[i];
                var currentTestNum = i + 1;

                string output = null;
                try
                {
                    output = program.Run(currentTestCase.Input);
                }
                catch (RunExceptions.InvalidAction ex)
                {
                    return new CheckResults.Task.FailedTest.InvalidAction(currentTestNum, currentTestCase.Input);
                }
                catch (RunExceptions.RuntimeError ex)
                {
                    return new CheckResults.Task.FailedTest.RuntimeError(currentTestNum, currentTestCase.Input, ex.ErrorMessage, ex.Line, ex.Column);
                }
                catch (RunExceptions.TimeLimitExceeded ex)
                {
                    return new CheckResults.Task.FailedTest.TimeLimitExceeded(currentTestNum, currentTestCase.Input, ex.TimeLimit);
                }

                var testCaseCheckResult = currentTestCase.Check(output);
                if (testCaseCheckResult is CheckResults.TestCase.WrongAnswer)
                {
                    return new CheckResults.Task.FailedTest.WrongAnswer(
                        currentTestNum,
                        currentTestCase.Input,
                        testCaseCheckResult as CheckResults.TestCase.WrongAnswer);
                }
            }

            return new CheckResults.Task.Accepted();
        }
    }
}